/*
 *  Openmysee
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 */
				 
#include "echo.h"

static int NumNewOrder;
static struct Channel *OrderHash[MAX_CHANNEL];
static struct Channel *OrderList;
extern char *PREFIX;

static inline void buildOrderPath (char *buf, int len, char *md5)
{
	snprintf (buf, len, "%s/%s/%.2s/", PREFIX, ORDER_PREFIX, md5);
	mkdir (buf, 0777);
	strcat (buf, md5);
}

/* DB format: MD5->[file_size(int)][filename_size(int)]filename */
static FILE *openOrder (char *md5, off_t *size)
{
	struct stat statbuf;
	char fname[MAX_DATA];

	if (*md5 == 0)
		return NULL;
	buildOrderPath (fname, CHNLURL_LEN, md5);
	if (stat (fname, &statbuf) != 0)
		return NULL;
	*size = statbuf.st_size;
	return fopen (fname, "r");
}

struct Channel *newOrder (char *md5)
{
	int i, id;
	off_t size;
	FILE *db;
	struct Channel *p, *q;

	if ((db=openOrder (md5, &size)) == NULL)
		return NULL;
	if (NumNewOrder >= MAX_CHANNEL)
	{
		for (i=0; i<MAX_CHANNEL; i++)
		{
			for (p=OrderHash[i]; p; p=p->next)
			{
				if (p->numjob == 0)
					break;
			}
			if (p) break;
		}
		if (!p) return (struct Channel *)0;
		if (OrderHash[i] == p)
		{
			OrderHash[i] = NULL;
		} else
		{
			for (q=OrderHash[i]; q && q->next != p; q=q->next);
			assert (q);
			q->next = p->next;
		}
		if (p->db) fclose (p->db);
	} else
	{
		NumNewOrder ++;
		p = (struct Channel *)calloc (sizeof (struct Channel), 1);
	}
	memcpy (p->channel_md5, md5, MD5_LEN);
	p->maxblocksize = DEFAULT_BLOCK;
	p->pcinfo = NULL;

	id = hash_str (p->channel_md5, MD5_LEN);
	PDEBUG("newOrder hash %.32s to %d.\n", p->channel_md5, id);
	p->downsize = size;
	p->upsize = 0;
	p->db = db;
	p->next = OrderHash[id];
	OrderHash[id] = p;
	p->lnext = OrderList;
	OrderList = p;
	
	return p;
}

int locate_order_by_id (struct Channel *c, uint32_t id, char *buf, int max)
{
	long long size;
	assert (buf && c);
	if ((size = c->downsize - id*DEFAULT_BLOCK) >= DEFAULT_BLOCK)
		size = DEFAULT_BLOCK;
	else if (size <= 0)
	{
		return -1;
	}
	if (max <= size) return -2;
	if (fseeko (c->db, ((off_t)DEFAULT_BLOCK)*((off_t)id), SEEK_SET) != 0)
	{
		PDEBUG ("fseek failed.\n");
		return -1;
	}
	if (fread (buf, size, 1, c->db) == 1)
	{
		c->upsize += size;
		return size;
	}
	return -1;
}

struct Channel *findOrder (char *name, int len)
{
	return getChannel (OrderHash, name, len);
}

void freeOrder (struct Channel *pc, void *p)
{
	freeChannel (OrderHash, &OrderList, &NumNewOrder, pc);
}

void freeAllOrder ()
{
	apply_hash (OrderHash, freeOrder, NULL);
}
